﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace DC.DCHierarchy.KnownTypes
{
    [DataContract]
    [KnownType(typeof(Contact))]
    public class Address
    {
        public string Country { get; set; }
        public string Province { get; set; }
        public string City { get; set; }
        public string Street { get; set; }
    }

    [DataContract]
    public class Contact : Address
    {
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
        [DataMember]
        public string PhoneNumber { get; set; }
        [DataMember]
        public string CarNumber { get; set; }
        [DataMember]
        public Address Address { get; set; }
    }

    [DataContract]
    public class Customer
    {
        public string Company { get; set; }
        public Contact Contact { get; set; }
    }

    /*
      * C#允许用子类来替换基类。但是这不适用于WCF操作。默认情况下，不能用数据契约的子类去替换基类。
      */

    [ServiceContract]
    public interface IServiceContract
    {
        //这里不能传递Contact对象，即便Contact是Address的子类。
        //因为传递的不是对象引用，而是对象状态。假如下面的代码传递的是Contact对象，服务并不知道它应该反序列化状态的Contact部分。
        [OperationContract]
        void AddAddress(Address address);

        //这里不能返回Contact对象
        //同理，如果GetAddress()返回Contact对象而不是Address对象，客户端也不知道该如何反序列化它，因为它能识别的类型是Address而不是Contact;
        [OperationContract]
        Address[] GetAddress();
    }

    /*
     * 解决以上问题的方法是使用KnowTypeAttribute特性明确的告知WCF关于Contact类的信息。
     * KnowType特性允许开发者制定数据契约能够接收的子类：
     * 
     * 在宿主端，如果将KnowType特性应用在基类上，会影响所有的契约与操作。它能跨越所有的服务和终结点，
     * 允许服务接收子类，而不是基类。此外，应用KnowType特性就可以再元数据中包含子类，这样客户端就具
     * 有了子类的定义，能够传递子类而不是基类。如果客户端同样将KnowType特性应用在基类的副本对象上，
     * 那么它就能够依次接收服务返回的已知子类对象。
     */


    /*
     * 使用KnowType 的缺陷在于，它使得范围过于宽泛。因此，WCF提供了ServiceKnownTypeAttribute特性。可以
     * 将ServiceKnownType应用在服务端的指定操作上。而且，只有这样才能够接收已知的子类。其他的操作不能接收该子类型。
     * 不论将ServiceKnownType应用到操作上还是契约级，导出的元数据和生成的代理类都不会包含它的内容，而只会包含应用 KnownType特性的基类。
     * 可以手动修改客户端代理类，通过删去基类的KnownType, 再将ServiceKnownType应用到契约的相应层级上，就可以准确地反映服务端的语义。
     * 
     * 通过同时应用KnownType和ServiceKnownType，可以重复通知WCF需要多个已知类型：
     * 
     * WCF通过反射收集数据契约的所有已知类型，然后检查提供的参数看是否具有任意的一种已知类型。
     */

    //[ServiceContract]
    //[ServiceKnownType(typeof(Contact))] //ServiceKnownType应用在契约级时，该契约以及实现该契约的所有服务包含的所有操作都能够接收已知的子类。
    //[ServiceKnownType(typeof(Customer))] //应用多个
    //public interface IServiceContract
    //{
    //    [OperationContract]
    //    [ServiceKnownType(typeof(Contact))] //只有该方法可以接受子类作为参数
    //    void AddAddress(Address address);

        
    //    [OperationContract]
    //    Address[] GetAddress();
    //}
}
